// 参考了Zinx框架
package nodex

import (
	"fmt"
	"gitee.com/zhongguo168a/go-nodex/dbx"
	"gitee.com/zhongguo168a/go-nodex/dbx/mongox"
	"gitee.com/zhongguo168a/go-nodex/logx"
	"gitee.com/zhongguo168a/go-nodex/nodex/filedb"
	"gitee.com/zhongguo168a/go-nodex/nodex/memdb"
	"gitee.com/zhongguo168a/go-nodex/nodex/models"
	"gitee.com/zhongguo168a/go-nodex/nodex/server"
	"gitee.com/zhongguo168a/gocodes/datax"
	"gitee.com/zhongguo168a/gocodes/myx/errorx"
	"gitee.com/zhongguo168a/gocodes/myx/identx"
	"gitee.com/zhongguo168a/gocodes/myx/scale"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"reflect"
	"strconv"
	"sync"
	"time"
)

var n = NewNode()

func NewNode() (obj *Node) {
	obj = &Node{
		routeMap:      map[string]*Router{},
		routeGroupMap: map[string]*RouteGroup{},
	}
	obj.servers = newServerSet()
	return
}

// 服务器节点
// 一个节点可提供多个服务器入口
// todo: 增加失败重试队列
type Node struct {
	config          *NodeConfig
	mutex           sync.RWMutex
	routeMap        map[string]*Router
	routeGroupMap   map[string]*RouteGroup
	servers         *serverSet
	serverConfigs   *models.ServerConfigList
	databaseConfigs *models.DatabaseConfigList
	nodeModel       *models.Node
	tickit          *models.Ticket
	initComplete    func()
}

func OnInitComplete(f func()) {
	n.initComplete = f
}
func GetDatabses() *models.DatabaseConfigList {
	return n.databaseConfigs
}

func GetServerConfigs() *models.ServerConfigList {
	return n.serverConfigs
}

func GetName() string {
	return n.config.Name
}

func GetChannel() string {
	return n.config.Channel
}

func GetIdent() string {
	return strconv.Itoa(n.tickit.Key)
}

func Dispose() {
	n.tickit.Dispose()
}

func Init(conf *NodeConfig) (err error) {
	n.config = conf

	// 初始化文件数据库
	{
		dbpath := conf.FileDBPath
		if dbpath == "" {
			dbpath = "./" + conf.Name + ".db"
		}

		err = filedb.Init(dbpath)
		if err != nil {
			return errorx.Wrap(err, "初始化文件数据库")
		}

		fmt.Printf("[系统] 初始化文件数据库成功，路径[%v]\n", dbpath)
	}
	// 初始化memcache
	{
		err = memdb.Init()
		if err != nil {
			return errorx.Wrap(err, "初始化内存数据库")
		}
		fmt.Printf("[系统] 初始化内存数据库成功\n")
	}
	// 初始化数据库
	dbx.RegisterDB(conf.Database)
	// 数据表通常导入包时就已经注册，也可通过运行节点钱注册。
	dbx.Init()

	// 获取门票
	{
		ticket, findErr := models.FindTicketWithLocal("node")
		if findErr != nil {
			return errorx.Wrap(findErr, "获取门票")
		}
		n.tickit = ticket
		err = ticket.KeeyAlive()
		if err != nil {
			return errorx.Wrap(err, "KeeyAliveTicket")
		}
		fmt.Printf("[系统] 获取并设置节点编号[%v]\n", GetIdent())
	}
	// 初始化 idmaker
	{
		nid := scale.DecimalTo(n.tickit.Key, 62)
		identx.SetNode(nid)
		fmt.Printf("[系统] 设置idmaker的节点为[%v]\n", nid)
	}

	//nodeModel, err := models.C初始化节点模型()
	//if err != nil {
	//	err = errorx.New("初始化节点模型")
	//	return
	//}
	//n.nodeModel = nodeModel

	_, updateerr := mongox.NewRequestByName(n.nodeModel.DBTable()).UpdateOne(
		n.nodeModel.DBQueryName(conf.Name),
		bson.M{
			"$set": bson.M{
				"State":      models.Starting,
				"Error":      "",
				"UpdateTime": time.Now().Unix(),
			},
		})
	if updateerr != nil {
		return errorx.Wrap(updateerr, "update node")
	}

	defer func() {
		_, updateerr2 := mongox.NewRequestByName(n.nodeModel.DBTable()).UpdateOne(
			n.nodeModel.DBQueryName(conf.Name),
			bson.M{
				"$set": bson.M{
					"State": func() (x models.NodeState) {
						if err != nil {
							x = models.Stop
						} else {
							x = models.Runing
						}
						return
					}(),
					"Error": func() (x string) {
						if err != nil {
							x = err.Error()
						}
						return
					}(),
					"UpdateTime": time.Now().Unix(),
				},
			})
		if updateerr2 != nil {
			err = errorx.Wrap(updateerr2, "update node")
			return
		}
	}()

	{
		// 处理数据库
		n.databaseConfigs, err = models.FindDatabaseConfigList()
		if err != nil {
			return errorx.Wrap(err, "FindDatabaseConfigList")
		}

		remoteHas := n.databaseConfigs.List.ContainsCond(func(index int, item *models.DatabaseConfig) bool {
			return item.Name == conf.Database.Name
		})
		if !remoteHas {
			database := models.NewDatabaseConfig()
			database.Id = primitive.NewObjectID().Hex()
			database.Name = conf.Database.Name
			database.Addr = conf.Database.Addrs[0].IP + ":" + conf.Database.Addrs[0].Port
			database.Kind = models.DatabaseKind_Mongo

			err = mongox.NewRequestByName(models.TableDatabase).InsertOne(database)
			if err != nil {
				err = errorx.Wrap(err, fmt.Sprintf("create database [name=%v]", database.Name))
				return
			}
			n.databaseConfigs.List.Add(database)
		}
	}

	// 处理服务器
	{
		servers, newerr := models.NewServerConfigListBySlice(conf.Servers)
		if newerr != nil {
			err = errorx.Wrap(newerr, "new server selector")
			return
		}

		n.serverConfigs = models.NewServerConfigList()
		err = mongox.NewRequestByName(n.serverConfigs.DBTable()).DeleteMany(n.serverConfigs.DBQueryAll())
		if err != nil {
			err = errorx.Wrap(err, "remove server configs")
			return
		}

		defaultConfig := models.NewServerConfig()
		for _, config := range servers.List.Slice() {
			if config.MaxConn == 0 {
				config.MaxConn = defaultConfig.MaxConn
			}
			if config.MaxPacketSize == 0 {
				config.MaxPacketSize = defaultConfig.MaxPacketSize
			}
			if config.MaxMsgChanLen == 0 {
				config.MaxMsgChanLen = defaultConfig.MaxMsgChanLen
			}
			config.Id = primitive.NewObjectID().Hex()
			config.Node = conf.Name
			err = mongox.NewRequestByName(config.DBTable()).InsertOne(config)
			if err != nil {
				err = errorx.Wrap(err, fmt.Sprintf("create server [name=%v]", config.Name))
				return
			}

			n.serverConfigs.List.Add(config)
		}

		for _, val := range n.serverConfigs.List.Slice() {
			err = n.createServer(val)
			if err != nil {
				return
			}
		}
	}

	// 处理路由
	{
		for index, val := range conf.Routes {
			router, newerr := n.NewRouter(val)
			if newerr != nil {
				err = errorx.Wrap(newerr, "NodeConfig.Routes", datax.M{"index": index})
				return
			}
			err = n.createRouter(router)
			if err != nil {
				err = errorx.Wrap(err, "NodeConfig.Routes", datax.M{"index": index})
				return
			}
		}
	}

	// 处理服务
	{
		for _, val := range conf.RouteGroup {
			s, newerr := NewRouteGroup(val)
			if newerr != nil {
				err = errorx.Wrap(newerr, "new service")
				return
			}

			if val.RouteHandler == nil {
				err = errorx.New("RouteGroup: RouteHandler is nil", datax.M{"name": s.Name})
				return
			}

			err = n.createRouteGroup(s)
			if err != nil {
				err = errorx.Wrap(err, "create service")
				return
			}
		}
	}

	if conf.PreServe != nil {
		err = conf.PreServe()
		if err != nil {
			err = errorx.Wrap(err, "preserve")
			return
		}
	}
	if n.initComplete != nil {
		n.initComplete()
	}

	return nil
}

func Run() (err error) {
	err = n.serve()
	if err != nil {
		err = errorx.Wrap(err, "serve")
		return
	}
	return
}

func (n *Node) serve() (err error) {
	n.servers.data.Range(func(key, value interface{}) bool {
		server := value.(IServer)
		go server.Serve()
		return true
	})
	_, err = mongox.NewRequestByName(n.nodeModel.DBTable()).UpdateOne(n.nodeModel.DBQueryName(n.config.Name),
		bson.M{
			"$set": bson.M{
				"State":      models.Starting,
				"Error":      "",
				"UpdateTime": time.Now().Unix(),
			},
		})
	if err != nil {
		err = errorx.Wrap(err, "update node")
		return
	}
	return
}

func (n *Node) createRouteGroup(service *RouteGroup) (err error) {
	refval := reflect.ValueOf(service.RouteHandler)
	reftyp := refval.Type()
	for i := 0; i < refval.NumMethod(); i++ {
		method := refval.Method(i)
		mtyp := reftyp.Method(i)
		r := &RouterConfig{
			AtServers:      service.AtServers,
			Path:           service.Name + "/" + mtyp.Name,
			ChanRPC:        service.RPCSelector,
			CallMode:       service.CallMode,
			ContextCreator: service.UseContextCreator,
			Handler:        method,
			EnableHttpGet:  service.EnableHttpGet,
		}

		logx.Info("register route: ", r.Path)

		router, newerr := n.NewRouter(r)
		if newerr != nil {
			err = errorx.Wrap(newerr, "new router")
			return
		}
		createerr := n.createRouter(router)
		if createerr != nil {
			err = errorx.Wrap(createerr, "create router")
			return
		}
	}

	n.routeGroupMap[service.Name] = service
	return
}

func (n *Node) createRouter(router *Router) (err error) {
	conf := router.Config
	for _, val := range conf.AtServers {
		server := n.servers.Get(val)
		if server == nil {
			err = errorx.New("RouterConfig.AtServers 未发现服务器", datax.M{"name": val})
			return
		}
		server.AddRouter(conf.Path, router)
	}
	n.routeMap[router.Config.Path] = router
	return
}

func (n *Node) createServer(conf *models.ServerConfig) (err error) {
	var (
		srv server.IServer
	)

	icreator, hasCreator := serverCreator.Load(conf.Mode)
	if !hasCreator {
		return errorx.New(fmt.Sprintf("server creator not found [mode=%v]", conf.Mode))
	}

	creator := icreator.(func(conf *models.ServerConfig) server.IServer)
	srv = creator(conf)
	//
	//switch conf.Mode {
	//case models.HTTP:
	//	inst = http.NewServer(&serverz.Config{
	//		Host:          ip,
	//		Port:          conf.Port,
	//		Name:          conf.Name,
	//		MaxPacketSize: conf.MaxPacketSize,
	//		MaxConn:       conf.MaxConn,
	//	})
	//	inst.SetMsgHandle(NewMsgHandle(n, server))
	//case models.TCP:
	//case models.WEBSOCKET:
	//	inst = ws.NewServer(&serverz.Config{
	//		Host:          ip,
	//		Port:          conf.Port,
	//		Name:          conf.Name,
	//		MaxPacketSize: conf.MaxPacketSize,
	//		MaxConn:       conf.MaxConn,
	//	})
	//	inst.SetMsgHandle(NewMsgHandle(n, server))
	//case models.RPC:
	//	inst = rpc.NewServer(&serverz.Config{
	//		Host:          ip,
	//		Port:          conf.Port,
	//		Name:          conf.Name,
	//		MaxPacketSize: conf.MaxPacketSize,
	//		MaxConn:       conf.MaxConn,
	//	})
	//	inst.SetMsgHandle(NewMsgHandle(n, server))
	//}

	n.servers.Add(srv)

	return
}
